package com.example.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.*;

/**
 * @author yzh
 * @version 1.0
 * @date 2021/7/27 13:54
 */
//@ServerEndpoint("/websocket/{sid}")
@ServerEndpoint("/websocket")
@Component
@Slf4j
public class WebSocketServer {
    // 维护连接
    private static CopyOnWriteArraySet<WebSocketServer> webSocketServers = new CopyOnWriteArraySet<>();
    // 与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;
    // 定义一个线程池，用来发送数据
    private static final ThreadPoolExecutor POOL = new ThreadPoolExecutor(3, 5,
            5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5), Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    /**
     * 连接建立成功调用的方法
     * @PathParam("sid") String sid
     * @param session
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketServers.add(this);
        log.info("集合大小: {}", webSocketServers.size());
        log.info("有窗口开始监听");
        this.sendMsg("连接成功");
    }

    @OnMessage
    public void onMessage(String msg, Session session) {
        log.info("来自 {} 的消息: {}", session, msg);
        // 发送消息
        this.sendMsg("服务端返回的消息");
    }

    @OnError
    public void onError(Session session, Throwable e) {
        log.error("{} 发生错误", session);
        e.printStackTrace();
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketServers.remove(this);
        log.info("onClose -- 集合大小: {}", webSocketServers.size());
        log.info("连接关闭");
    }

    /**
     * 服务端主动发送消息
     * @param msg
     */
    public void sendMsg(String msg) {
        try {
            this.session.getBasicRemote().sendText(msg);
        } catch (IOException e) {
            log.error("webSocket IO 异常");
        }
    }

    /**
     * 把消息推送到指定的 sid
     * @param msg
     */
//    public void sendInfo(String sid, String msg) {
    public void sendInfo(String msg) {
        msg = msg == null ? "" : msg;
        for (WebSocketServer w : webSocketServers) {
            String finalMsg = msg;
            log.info("推送消息到窗口: {}, 推送内容: {}", w.session, msg);
            POOL.execute(() -> w.sendMsg(finalMsg));
        }
    }

}
